package com.sjs.boot.dynamic.core.config.method.parameter;

import com.sjs.boot.dynamic.core.config.method.MethodConfig;
import com.sjs.boot.dynamic.core.run.MethodContext;
import com.sjs.boot.dynamic.util.ValParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;
import java.util.regex.Pattern;

/**
 * @author uto_dev_008
 * @desc
 * @since 2021/11/30 18:31
 */
public class StringParameterInfo extends ParameterInfo {
    private static final Logger LOG = LoggerFactory.getLogger(StringParameterInfo.class);
    private static final String PROPERTY_REGULAR  = "regular";
    private static final String PROPERTY_NOSQL    = "noSql";
    private static final String PROPERTY_NOHTML   = "noHtml";
    private static final String PROPERTY_NOLINE   = "noLine";
    private static final String PROPERTY_NOQUOT   = "noQuot";
    private static final String PROPERTY_CODEMODE = "codeMode";
    private static final String PROPERTY_STRICT   = "strict";
    private static final String PROPERTY_LEN      = "len";
    private static final String PROPERTY_TRIM     = "trim";
    private static final String PROPERTY_TAIL     = "tail";

    protected int minLen = 0;
    protected int maxLen = 255;
    protected String defaultVal = null;
    protected Pattern regular = null;
    protected String tail = null;

    protected boolean noSql = false;
    protected boolean noHtml = false;
    protected boolean noLine = false;
    protected boolean noQuot = false;
    protected boolean strict = false;
    protected boolean trim = false;  //是否去除首尾空格

    private static enum CodeMode{NONE, ENCODE, DECODE};

    private CodeMode codeMode = CodeMode.NONE;

    private String claim = "";

    @Override
    protected boolean parseExt(String version, Map<String, Object> para, MethodConfig mc) {
        if(para.containsKey(PROPERTY_REGULAR)) {
            this.regular = Pattern.compile(ValParser.getAsStr(para, PROPERTY_REGULAR));
        }

        this.claim = "String,length";
        int len = ValParser.getAsInt(para, PROPERTY_LEN, -1);
        if(len > 0) {
            this.maxLen = len;
            this.minLen = len;
            this.claim += "=" + len;
        } else {
            this.maxLen = ValParser.getAsInt(para, PROPERTY_MAX, this.maxLen);
            this.minLen = ValParser.getAsInt(para, PROPERTY_MIN, this.minLen);
            this.claim += "(" + this.minLen + "," + this.maxLen + ")";
        }

        if(para.containsKey(PROPERTY_DEFAULT)) {
            this.defaultVal = ValParser.getAsStr(para, PROPERTY_DEFAULT);
        }

        if(para.containsKey(PROPERTY_STRICT)) {
            this.strict = ValParser.getAsBool(para, PROPERTY_STRICT);
            if(this.strict) {
                this.noSql = true;
                this.noLine = true;
                this.noQuot = true;
                this.noHtml = true;
            }
        }

        String codeMode = ValParser.getAsStr(para, PROPERTY_CODEMODE, "NONE").toUpperCase();
        try {
            this.codeMode = CodeMode.valueOf(codeMode);
        } catch(Exception e) {
            LOG.error("Invalid codeMode setted", e);
            return false;
        }

        //设置了加解密，且没有设置log，则log默认为false
        if(this.codeMode != CodeMode.NONE && !para.containsKey(PROPERTY_LOG)) {
            this.log = false;
        }

        this.noSql = ValParser.getAsBool(para, PROPERTY_NOSQL, this.noSql);
        this.noLine = ValParser.getAsBool(para, PROPERTY_NOLINE, this.noLine);
        this.noHtml = ValParser.getAsBool(para, PROPERTY_NOHTML, this.noHtml);
        this.noQuot = ValParser.getAsBool(para, PROPERTY_NOQUOT, this.noQuot);
        this.trim = ValParser.getAsBool(para, PROPERTY_TRIM, this.trim);
        this.tail = ValParser.getAsStr(para, PROPERTY_TAIL, null);

        if(this.must) {
            this.claim += ",must";
        }
        if(this.regular != null) {
            this.claim += ',' + this.regular.toString();
        }
        if(this.noSql) {
            this.claim += ",no %?";
        }
        if(this.noHtml) {
            this.claim += ",no <>";
        }
        if(this.noQuot) {
            this.claim += ",no \'\\\"";
        }
        if(this.noLine) {
            this.claim += ",no line-feed and return";
        }

        return true;
    }

    @Override
    protected boolean checkExt(MethodContext context, Object ele) {
        String val;
        if(ele instanceof String) {
            val = (String)ele;
        } else {
            val = ele.toString();
        }

        int len = val.length();
        if(len < this.minLen || len > this.maxLen) {
            return false;
        }

        if(this.noSql) {
            if(val.indexOf('?') >= 0 || val.indexOf('%') >= 0) {
                return false;
            }
        }

        if(this.noHtml) {
            if(val.indexOf('<') >= 0 || val.indexOf('>') >= 0) {
                return false;
            }
        }

        if(this.noLine) {
            if(val.indexOf('\n') >= 0 || val.indexOf('\r') >= 0) {
                return false;
            }
        }

        if(this.noQuot) {
            if(val.indexOf('\"') >= 0 || val.indexOf('\'') >= 0) {
                return false;
            }
        }

        if(this.regular != null && !this.regular.matcher(val).matches()) {
            return false;
        }

        return true;
    }


    private static String code(String val, CodeMode mode) {
        switch(mode) {
            case ENCODE:
//                return Encod.encode(val);
                return val;
            case DECODE:
//                return EncryptUtil.decode(val);
                return val;
            default:
                return val;
        }
    }

    public static String serialize(String str) {
        return str.replace("\r", "").replace("\\", "\\\\").replace("\"", "\\\"").replace("\n", "\\n");
    }

    @Override
    public String getClaim() {
        return claim;
    }

    @Override
    public Object getValue(MethodContext context) {
        String val = ValParser.parseString(context.getParameters().get(this.name), null);

        if(val != null) {
            if(this.maps != null) {
                val = this.maps.get(val).toString();
            } else if(this.trim) { //如果设置了映射，最好自己做trim
                val = val.trim();
            }

            if(this.tail != null) {
                val += this.tail;
            }

            return code(val, this.codeMode);
        } else if(this.defaultVal != null) {
            return this.defaultVal;
        } else {
            return null;
        }
    }
}
